{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parte 09 - Introdução à Programas Criptografados\n",
    "\n",
    "\n",
    "Acredite ou não, é possível realizar cálculos com dados criptografados. Em outras palavras, é possível executar um programa onde **TODAS as variáveis** estão **criptografadas**! \n",
    "\n",
    "Neste tutorial, nós vamos falar sobre as ferramentas básicas de computação criptografada. Em particular, nós vamos focar em uma abordagem popular chamada Computação Multiparte Segura (i.e. **Secure Multi-Party Computation ou SMPC**). Nesta lição, você aprenderá como criar uma calculadora criptografada que pode executar cálculos com números criptografados.\n",
    "\n",
    "Autores:\n",
    "- Andrew Trask - Twitter: [@iamtrask](https://twitter.com/iamtrask)\n",
    "- Théo Ryffel - GitHub: [@LaRiffle](https://github.com/LaRiffle)\n",
    "\n",
    "Tradução:\n",
    "- Jeferson Silva - Github: [@jefersonf](https://github.com/jefersonf)\n",
    "\n",
    "Referências: \n",
    "- Morten Dahl - [Blog](https://mortendahl.github.io) - Twitter: [@mortendahlcs](https://twitter.com/mortendahlcs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Passo 1: Criptografia Usando Computação Multiparte Segura\n",
    "\n",
    "SMPC é, à primeira vista, uma forma bastante estranha de \"criptografia\". Em vez de usar uma chave pública/privada para criptografar uma variável, cada valor é dividido em várias `partes`, cada uma das quais opera como uma chave privada. Normalmente, essas `partes` serão distribuídas entre 2 ou mais proprietários. Assim, para decifrar a variável, todos os proprietários devem concordar em permitir a sua descriptografia. Em essência, todos têm uma chave privada.\n",
    "\n",
    "### Encrypt()\n",
    "\n",
    "Então, digamos que queríamos \"encriptar\" uma variável `x`, podíamos fazê-lo da seguinte forma.\n",
    "\n",
    "> A criptografia não utiliza números reais ou de ponto flutuante mas um espaço matemático chamado de [integer quotient ring](http://mathworld.wolfram.com/QuotientRing.html) (anel quociente inteiro) que são basicamente os inteiros entre `0` e `Q-1`, onde `Q` é primo e \"grande o suficiente\" tal que o espaço contenha todos os números que nós usamos em nossos experimentos. Na prática, dado um valor `x` inteiro, fazemos `x % Q` (resto da divisão inteira) para que esteja contido no \"anel\". (É por isso que evitamos usar o número `x` > `Q`)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "Q = 1234567891011"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 25"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "def encrypt(x):\n",
    "    share_a = random.randint(-Q,Q) # Entenda share_a como parte A de x, idem para B, e C.\n",
    "    share_b = random.randint(-Q,Q)\n",
    "    share_c = (x - share_a - share_b) % Q\n",
    "    return (share_a, share_b,  share_c)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(890804432397, -2305631655, 346069090294)"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "encrypt(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Como você pode ver aqui, nós dividimos nossa variável `x` em 3 partes diferentes, que poderiam ser enviadas para 3  diferentes proprietários de dados.\n",
    "\n",
    "### Decrypt()\n",
    "\n",
    "Se quiséssemos decifrar essas três partes, poderíamos simplesmente juntá-las e pegar o módulo do resultado (mod Q)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "def decrypt(*shares):\n",
    "    return sum(shares) % Q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "a,b,c = encrypt(25)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "25"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "decrypt(a, b, c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "É importante notar que se tentarmos decifrar com apenas duas partes, a descriptografia não funciona!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "778460474681"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "decrypt(a, b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Assim, precisamos que todos os proprietários participem para decifrar o valor. É desta forma que as partes agem como chaves privadas, todas as quais devem estar presentes para decifrar um valor."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Passo 2: Aritmética Básica Usando SMPC\n",
    "\n",
    "No entanto, a propriedade verdadeiramente extraordinária da Computação Multiparte Segura é a capacidade de realizar cálculos **enquanto as variáveis ainda estão criptografadas**. Vamos demonstrar uma simples adição abaixo."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = encrypt(25)\n",
    "y = encrypt(5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, y):\n",
    "    z = list()\n",
    "    # o primeiro worker adiciona as suas partes\n",
    "    z.append((x[0] + y[0]) % Q)\n",
    "    \n",
    "    # o segundo worker adiciona as suas partes\n",
    "    z.append((x[1] + y[1]) % Q)\n",
    "    \n",
    "    # o terceiro worker adiciona as suas partes\n",
    "    z.append((x[2] + y[2]) % Q)\n",
    "    \n",
    "    return z"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "30"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "decrypt(*add(x,y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Sucesso!!!\n",
    "\n",
    "E aqui o temos! Se cada _worker_ (separadamente) somar suas partes, então as partes resultantes serão decifradas para o valor correto (25 + 5 === 30). \n",
    "\n",
    "Acontece que, existem protocolos SMPC que podem permitir este cálculo criptografado para as seguintes operações:\n",
    "- Adição (que acabamos de ver)\n",
    "- Multiplicação\n",
    "- Comparação\n",
    "\n",
    "\n",
    "e usando estas três operações básicas, podemos fazer cálculos arbitrários!!!\n",
    "\n",
    "Na próxima seção, vamos aprender como usar a biblioteca PySyft para realizar estas operações!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Passo 3: SMPC Usando PySyft\n",
    "\n",
    "Nas seções anteriores, nós demostramos algumas intuições básicas em torno do SMPC e como o método deve funcionar. Contudo, na prática, não queremos ter de ser nós próprios a escrever à mão todas as operações primitivas ao escrever os nossos programas criptografados. Então, nesta seção vamos passar pelo básico de como fazer computação criptografada usando o PySyft. Em particular, vamos focar em como fazer as 3 operações básicas mencionados anteriormente: adição, multiplicação, e comparação.\n",
    "\n",
    "Primeiro, precisamos criar alguns _Workers Virtuais_ (esperamos que você já esteja familiarizado com os nossos tutoriais anteriores). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import syft as sy\n",
    "hook = sy.TorchHook(torch)\n",
    "\n",
    "bob = sy.VirtualWorker(hook, id=\"bob\")\n",
    "alice = sy.VirtualWorker(hook, id=\"alice\")\n",
    "bill = sy.VirtualWorker(hook, id=\"bill\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Criptografia e Descriptografia Básica\n",
    "\n",
    "Criptografar é tão simples como tomar qualquer tensor PySyft e chamar `.share()`. Descriptografar é tão simples como chamar `.get()` na variável compartilhada."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([25])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([25])"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "encrypted_x = x.share(bob, alice, bill)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([25])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "encrypted_x.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Considerações sobre os Valores Criptografados\n",
    "\n",
    "Se olharmos mais de perto para os _workers_ Alice, Bob e Bill, podemos ver as partes que são criadas em cada um deles!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{}"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bob._objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([25]).share(bob, alice, bill)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([3212861001891376707])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Parte de Bob\n",
    "bobs_share = list(bob._objects.values())[0]\n",
    "bobs_share"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([61371170032936135])"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Parte de Alice\n",
    "alices_share = list(alice._objects.values())[0]\n",
    "alices_share"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1337453846503075087])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Parte de Bill\n",
    "bills_share = list(bill._objects.values())[0]\n",
    "bills_share"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "E se quiséssemos, poderíamos decifrar esses valores usando a mesma abordagem de que falamos anteriormente!!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([25])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Q = x.child.field\n",
    "\n",
    "(bobs_share + alices_share + bills_share) % Q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, when we called `.share()` it simply split the value into 3 shares and sent one share to each of the parties!\n",
    "\n",
    "Como você pode ver, quando chamamos `.share()` simplesmente dividimos o valor em 3 partes e o enviamos para cada um dos _workers_ !"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Aritmética Criptografada\n",
    "\n",
    "E agora você vê que nós podemos fazer aritmética sobre os valores subjacentes! A API é construída para que possamos simplesmente executar a aritmética como faríamos com os tensores PyTorch normais."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([25]).share(bob,alice)\n",
    "y = torch.tensor([5]).share(bob,alice)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([30])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z = x + y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([20])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z = x - y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Multiplicação Criptografada\n",
    "\n",
    "Para a multiplicação, precisamos de uma parte adicional que seja responsável pela geração consistente de números aleatórios (e que não colida com nenhuma das outras partes). Nós chamamos essa terceira parte de \"provedor de criptografia\". Para todos os propósitos, o provedor de criptografia é apenas um \"VirtualWorker\" adicional, mas é importante reconhecer que o provedor de criptografia não é um \"dono/proprietário\", pois ele não possui partes, mas é alguém que precisa ser confiável para não entrar em conluio com nenhuma das partes existentes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "crypto_provider = sy.VirtualWorker(hook, id=\"crypto_provider\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([125])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# multiplicação\n",
    "\n",
    "z = x * y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Você também pode fazer multiplicação matricial"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([[1, 2],[3,4]]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "y = torch.tensor([[2, 0],[0,2]]).share(bob,alice, crypto_provider=crypto_provider)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[2, 4],\n",
       "        [6, 8]])"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# multiplicação de matrizes\n",
    "\n",
    "z = x.mm(y)\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Comparação Criptografada\n",
    "\n",
    "Também é possível fazer comparações privadas entre valores privados. Contamos aqui com o protocolo _SecureNN_ , cujos detalhes podem ser encontrados [aqui](https://eprint.iacr.org/2018/442.pdf). O resultado da comparação é também um tensor privado compartilhado."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1])"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z = x > y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0])"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z = x <= y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z = x == y\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([1])"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "z = x == y + 20\n",
    "z.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Você também pode realizar operações de _maximum._"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([4])"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.tensor([2, 3, 4, 1]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "x.max().get()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([4, 3])"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = torch.tensor([[2, 3], [4, 1]]).share(bob,alice, crypto_provider=crypto_provider)\n",
    "max_values, max_ids = x.max(dim=0)\n",
    "max_values.get()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Parabéns!!! - Hora de se juntar a comunidade!\n",
    "\n",
    "Parabéns por concluir esta etapa do tutorial! Se você gostou e gostaria de se juntar ao movimento em direção à proteção de privacidade, propriedade descentralizada e geração, demanda em cadeia, de dados em IA, você pode fazê-lo das seguintes maneiras!\n",
    "\n",
    "### Dê-nos uma estrela em nosso repo do PySyft no GitHub\n",
    "\n",
    "A maneira mais fácil de ajudar nossa comunidade é adicionando uma estrela nos nossos repositórios! Isso ajuda a aumentar a conscientização sobre essas ferramentas legais que estamos construindo.\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### Junte-se ao Slack!\n",
    "\n",
    "A melhor maneira de manter-se atualizado sobre os últimos avanços é se juntar à nossa comunidade! Você pode fazer isso preenchendo o formulário em [http://slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### Contribua com o projeto!\n",
    "\n",
    "A melhor maneira de contribuir para a nossa comunidade é se tornando um contribuidor do código! A qualquer momento, você pode acessar a página de *Issues* (problemas) do PySyft no GitHub e filtrar por \"Projetos\". Isso mostrará todas as etiquetas (tags) na parte superior, com uma visão geral de quais projetos você pode participar! Se você não deseja ingressar em um projeto, mas gostaria de codificar um pouco, também pode procurar mais mini-projetos \"independentes\" pesquisando problemas no GitHub marcados como \"good first issue\".\n",
    "\n",
    "- [Projetos do PySyft](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3AProject)\n",
    "- [Etiquetados como Good First Issue](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### Doar\n",
    "\n",
    "Se você não tem tempo para contribuir com nossa base de códigos, mas ainda deseja nos apoiar, também pode se tornar um Apoiador em nosso Open Collective. Todas as doações vão para hospedagem na web e outras despesas da comunidade, como hackathons e meetups!\n",
    "\n",
    "[Página do Open Collective do OpenMined](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
